#include "stdafx.h"
#include <assert.h>
#include "ReadWriteLock.h"

//空闲
#define RW_IDLE 0x00

//读状态
#define RW_READ 0x01

//写状态
#define RW_WRITE 0x02

//成员初始化
ReadWriteLock::ReadWriteLock():readLockCount(0),
	lockStatus(0),
	readWaitLockCount(0),
	writeWaitLockCount(0),
	notifyEvent(NULL)
{
	//初始化临界资源
	InitializeCriticalSection(&(this->stLock));

	/*
		HANDLE CreateEventA(  
		  LPSECURITY_ATTRIBUTES lpEventAttributes, // SD  
		  BOOL bManualReset,                       // reset type  
		  BOOL bInitialState,                      // initial state  
		  LPCTSTR lpName                           // object name  
		);  
		lpEventAttributes：指向SECURITY_ATTRIBUTES结构体，此结构体决定函数的返回句柄是否可以让子进程继承。
			如果这个参数为NULL，这个句柄是不能继承的。一般情况下，这个参数设置为NULL。

		bManualReset：指定将创建的EVENT是自动复位还是手动复位。
			如果为TRUE，需要用ResetEvent(HANDLE)函数手动复位状态为无信号，即一旦改EVENT被设置成有信号，则它会一直等到ResetEvent调用时才为无信号状态。
			如果为FALSE，当一个有信号的等待线程被释放后，系统会自动复位状态为无信号状态。

		bInitialState：指定事件对象的初始状态。如果为TRUE，初始状态为有信号，否则为无信号。

		lpName：事件对象的名称，以字符串表示。名称的长度受MAX_PATH的限制，名称是大小写敏感的
	*/
	this->notifyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	assert(this->notifyEvent != INVALID_HANDLE_VALUE);
}

/*释放资源*/
ReadWriteLock::~ReadWriteLock()
{
	DeleteCriticalSection(&(this->stLock));

	//关闭事件对象
	CloseHandle(this->notifyEvent);
}

//是否定义了同步时钟
#ifdef USE_SYNCLOCK

/*读锁*/
void ReadWriteLock::readLock()
{
	//进入临界区，对需要保护的资源进行操作
	EnterCriticalSection(&(this->stLock));
}

/*写锁*/
void ReadWriteLock::writeLock()
{
	//进入临界区，对需要保护的资源进行操作
	EnterCriticalSection(&(this->stLock));
}

/*离开锁*/
void ReadWriteLock::unLock()
{
	//离开临界区
	LeaveCriticalSection(&(this->stLock));
}



#else

/*公共方法*/
void ReadWriteLock::common()
{
	//复位信号（重置为无信号状态）重新竞争
	ResetEvent(this->notifyEvent);

	//离开临界区
	LeaveCriticalSection(&(this->stLock));

	/* WaitForSingleObject函数用来检测hHandle事件的信号状态，
			在某一线程中调用该函数时，线程暂时挂起，如果在挂起的dwMilliseconds毫秒内，
			线程所等待的对象变为有信号状态，则该函数立即返回；如果超时时间已经到达dwMilliseconds毫秒，
			但hHandle所指向的对象还没有变成有信号状态，函数照样返回。

    	参数dwMilliseconds有两个具有特殊意义的值：0和INFINITE。
			若为0，则该函数立即返回；若为INFINITE，则线程一直被挂起，直到hHandle所指向的对象变为有信号状态时为止。
		 虽然 LeaveCriticalSection() 和 WaitForSingleObject() 之间有一个时间窗口,
				但是由于windows平台的事件信号是不会丢失的,所以没有问题.
	*/
	WaitForSingleObject(this->notifyEvent, INFINITE);
}

/*读锁*/
void ReadWriteLock::readLock()
{
	//是否等待返回
	bool isWaitReturn = false;

	while (1)
	{
		//进入临界区
		EnterCriticalSection(&(this->stLock));

		if (isWaitReturn)
		{
			//等待事件返回，重新竞争锁
			-- this->readWaitLockCount;
		}

		//如果空闲
		if (this->lockStatus == RW_IDLE)
		{
			//读锁直接得到控制权
			this->lockStatus = RW_READ;

			++this->readLockCount;

			//离开临界区
			LeaveCriticalSection(&(this->stLock));

			break;
		}
		else if (RW_READ == this->lockStatus)
		{
			//如果有线程正在写等待，那么一起等待
			if (this->writeWaitLockCount > 0)
			{
				++this->readWaitLockCount;

				this->common();

				//继续等待返回，事件重新竞争锁
				isWaitReturn = true;
			}
			else
			{
				//读锁计数
				++this->readLockCount;
				//离开临界区
				LeaveCriticalSection(&(this->stLock));
			}
		}
		else if (RW_WRITE == this->lockStatus)
		{
			//等待写锁释放
			++ this->writeWaitLockCount;

			this->common();

			//继续等待返回，事件重新竞争锁
			isWaitReturn = true;
		}
		else
		{
			assert(0);
			break;
		}
	}
}

/*写锁*/
void ReadWriteLock::writeLock()
{
	bool isWaitRewrit = false;

	while (1)
	{
		//进入临界区
		EnterCriticalSection(&(this->stLock));

		//
		if (isWaitRewrit)
		{
			
			--this->writeWaitLockCount;
		}

		if (RW_IDLE == this->lockStatus)
		{
			//空闲状态
			LeaveCriticalSection(&(this->stLock));

			this->lockStatus = RW_WRITE;

			break;
		}
		else if (RW_WRITE == this->lockStatus)
		{
			//写等待锁
			++this->writeWaitLockCount;

			this->common();

			isWaitRewrit = true;
		}
	}
}

/*离开锁*/
void ReadWriteLock::unLock()
{
	
	//先加锁
	EnterCriticalSection(&(this->stLock));

	if (this->readLockCount > 0)
	{
		//读锁解锁
		-- this->readLockCount;

		this->lockStatus = RW_IDLE;

		if (this->readLockCount == 0)
		{
			//是否有读等待锁 或者写等待锁
			if (this->readWaitLockCount > 0 || this->writeWaitLockCount > 0)
			{
				/*
				* 此时有锁请求正在等待,激活所有等待的线程.(手动事件).
				* 使这些请求重新竞争锁.
				*/
				SetEvent(this->notifyEvent);
			}
		}
	}

	else
	{
		//写等待锁解锁
		--this->writeWaitLockCount;

		this->lockStatus = RW_IDLE;

		if (this->writeWaitLockCount > 0 || this->readWaitLockCount > 0)
		{
			/*
			* 如果在占有互斥量stLock的情况下,触发事件,那么可能会使一些锁请求不能得到竞争机会.
			* 假设调用unlock时,另一个线程正好调用rlock或者wlock.如果不释放互斥量,只有之前已经等待的锁请求有机会获得锁控制权.
			*/
			SetEvent(this->notifyEvent);
		}
	}

	LeaveCriticalSection(&(this->stLock));
}

#endif


